#include <linux/init.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/device/class.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/wait.h>

struct device_node *node;
int irq_keys[3]; //
struct class *pCls;
int major;
wait_queue_head_t wq_head;  // 阻塞式io等待队列头
unsigned int condition = 0; // 条件变量
// char k_buf[4]={0};
int key_no; // 几号按键被按下了

ssize_t key_read(struct file *fp, char __user *ubuf, size_t size, loff_t *lf)
{
    int ret;
    printk("按键驱动_read\n");
    if (fp->f_flags & O_NONBLOCK)
    { // 非阻塞式
        return -EAGAIN;
    }
    else
    { // 阻塞式
        ret = wait_event_interruptible(wq_head, condition);
        if (ret)
        {
            printk("wait_event_interruptible失败\n");
            return -EFAULT;
        }
        ret = copy_to_user(ubuf, &key_no, size);
        if (ret)
        {
            // printk("copy_to_user失败\n");
            return -EFAULT;
        }
        // printk("copy_to_user成功\n");
        condition = 0;
        return size;
    }

    return 0;
}

int key_open(struct inode *ind, struct file *fp)
{
    printk("按键驱动_open\n");

    return 0;
}
int key_release(struct inode *ind, struct file *fp)
{
    printk("按键驱动_release\n");
    return 0;
}

struct file_operations fops = {
    .open = key_open,
    .release = key_release,
    .read = key_read};

/**
 * 中断处理函数
 */
irqreturn_t key_irq_handler(int irqno, void *dev)
{
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        if (irq_keys[i] == irqno)
        {
            break;
        }
    }
    key_no = i + 1;

    // 唤醒阻塞的进程
    condition = 1;
    wake_up_interruptible(&wq_head);
    return IRQ_HANDLED;
}

/**
 * 设备驱动入口函数
 */
static int __init mycdev_init(void)
{
    int ret, i;
    struct device *pDev;
    char name[10];
    printk("按键驱动_init\n");
    // 1、从设备树中获取按键设备节点
    node = of_find_node_by_path("/mykey");
    if (node == NULL)
    {
        printk("设备树节点解析失败\n");
        return -ENOENT;
    }
    printk("设备树节点解析成功\n");

    // 2、根据设备树节点信息解析出软中断号
    for (i = 0; i < 3; i++)
    {
        int irqno = irq_of_parse_and_map(node, i);
        if (irqno == 0)
        {
            printk("[key%i] 软中断号获取失败\n", i + 1);
            return -ENOENT;
        }
        // 3、注册中断处理函数
        sprintf(name, "key%i", i + 1);
        ret = request_irq(irqno, key_irq_handler, IRQF_TRIGGER_FALLING, name, NULL);
        if (ret)
        {
            printk("[key%i] 中断注册失败 irqno=[%d]\n", i + 1, irqno);
            return ret;
        }
        irq_keys[i] = irqno;
        printk("[key%i] 中断注册成功 irqno=[%d]\n", i + 1, irqno);
    }

    // 4、注册字符设备驱动
    major = register_chrdev(0, "key_drv", &fops);
    if (major < 0)
    {
        printk("字符设备注册失败\n");
        return -ENOENT;
    }
    printk("字符设备注册成功\n");

    // 5、u-dev的class和device自动创建
    pCls = class_create(THIS_MODULE, "key_drv");
    if (IS_ERR(pCls))
    {
        printk("class_create失败\n");
        return PTR_ERR(pCls);
    }
    printk("class_create成功\n");

    pDev = device_create(pCls, NULL, MKDEV(major, 0), NULL, "key_drv");
    if (IS_ERR(pDev))
    {
        printk("device_create失败 [%s]\n", name);
        return PTR_ERR(pDev);
    }
    printk("device_create成功 [%s]\n", name);
    // 6、初始化阻塞IO的等待队列
    init_waitqueue_head(&wq_head);

    printk("按键驱动初始化成功\n");

    return 0;
}

/**
 * 设备驱动出口函数
 */
static void __exit mycdev_exit(void)
{
    int i;
    printk("按键驱动_exit\n");
    // 1、注销中断处理函数 free_irq
    for (i = 0; i < 3; i++)
        free_irq(irq_keys[i], NULL);
    // 2、注销字符设备驱动 unregister
    unregister_chrdev(major, "key_drv");
    // 3、删除u-dev的class和device
    device_destroy(pCls, MKDEV(major, 0));
    class_destroy(pCls);
    // 4、释放设备树节点 of_node_put
    // 5、释放软中断号 irq_dispose_mapping
    printk("按键驱动退出成功\n");
}

module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
